/*-//GEN-BEGIN:copyright
 * ForBILE - Framework for Building Interactive Learning Environments
 * Computing Institute - Federal University of Alagoas - Brazil
 * Group of Otimization of the Web
 *
 * Copyright (C) 2007 Ig Bittencourt (ig.ibert@gmail.com)
 *		      Camila Nunes (camilapbnunes@hotmail.com)
 *	              Marcos Tadeu (zero.ufal@gmail.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *///GEN-END:copyright
package br.ufal.ic.forbile.agents;

import br.ufal.ic.forbile.agents.behaviours.GeneralActuator;
import br.ufal.ic.forbile.exceptions.ForBILEException;
import java.util.logging.Logger;
import br.ufal.ic.forbile.agents.behaviours.RegisterServices;
import br.ufal.ic.forbile.agents.mediator.behaviours.RequestPersistence;
import br.ufal.ic.forbile.agents.persistence.PersistenceOWLAgentInteraction;
import br.ufal.ic.forbile.infra.interaction.ontology.Hability;
import br.ufal.ic.forbile.infra.interaction.ontology.Service;
import br.ufal.ic.forbile.log.PersistenceOWLLog;
import br.ufal.ic.utils.Constants;
import br.ufal.ic.utils.RegionServer;
import br.ufal.ic.utils.SharedMem;
import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.domain.FIPAException;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ForBILEAgent.java
 *
 * <p>A full description of this class.
 * @author <a href="mailto:ig.ibert@gmail.com">ig</a>.
 * @author <a href="mailto:camilapbnunes@hotmail.com">camila</a>.
 * @version 0.1
 * @see SomeRelatedClass.
 * @since 0.1
 */
public abstract class ForBILEAgent extends Agent {
    
    // Constants -----------------------------------------------------
    
    public final long SENSOR_TIME = 1000;
    
    // Attributes ----------------------------------------------------
    
    protected Map<Agent, Map<String, List<String> > > agentServicesHabilities;
    protected Map<String, List<String> >              serviceHabilities;
    private String containerName;
    private String agentLocalName;
    // Static --------------------------------------------------------
    
    private static Logger logger;
    
    static {
        logger = Logger.getLogger(ForBILEAgent.class.getName());
    }
    
    // Constructors --------------------------------------------------
    
    /**
     * Creates a new instance of ForBILEAgent1
     * @param containerName the Jade container name
     * @param agentLocalName the agent local name
     */
    public ForBILEAgent(final String containerName, final String agentLocalName) {
        this.agentServicesHabilities = new
                HashMap<Agent, Map<String, List<String> > >();
        this.serviceHabilities = new HashMap<String, List<String> >();
        this.setContainerName(containerName);
        this.setAgentLocalName(agentLocalName);
    }
    
    // Public --------------------------------------------------------
    
    /**
     * Sets the agent container name.
     * @param containerName the Jade container name
     */
    public void setContainerName(final String containerName) {
        if ( (containerName.indexOf(":") != -1) || (containerName.indexOf("<") != -1) || (containerName.indexOf(">") != -1) ||
                (containerName.indexOf("/") != -1) || (containerName.indexOf("*") != -1) || (containerName.indexOf("|") != -1) ||
                (containerName.indexOf("?") != -1) ) {
            new ForBILEException("The container name syntax is incorrect. Use only ANSI characters.");
        } else {
            this.containerName = containerName;
        }
        
        
    }
    
    /**
     * Gets the agent container name.
     * @return The agent container name.
     */
    public String getContainerName(){
        return this.containerName;
    }
    
    /**
     * Sets the agent's habilities.
     * @param localName the agent local name
     */
    public void setAgentLocalName(final String agentLocalName){
        this.agentLocalName = agentLocalName;
    }
    
    public String getAgentLocalName(){
        return this.agentLocalName;
    }
    
    /**
     * Register the agent's services.
     *
     * @param agent The specific agent.
     */
    protected void registerAgentServices(final ForBILEAgent agent){
        addBehaviour(new RegisterServices(agent));
        addBehaviour(new GeneralActuator(getControllerAgent(), "", "START"));
    }
    
    /**
     * Configuring the interaction agent.
     *
     * @param interactionAgent The interaction agent.
     */
    protected void configureInteraction(final
            br.ufal.ic.forbile.infra.interaction.ontology.Agent interactionAgent) {
        List<String> habilities = null;                
        
        try {
            if ( interactionAgent.getSuperClass() != null ) {
                configureInteraction(interactionAgent.getSuperClass());
                
                for (Object s : interactionAgent.getSuperClass().getHas_Service()) {
                    Service service = (Service) s;
                    logger.info("Service super:" + service.getName());
                                        
                    if (service.getHas_Hability() != null) {
                        for (Object h : service.getHas_Hability()) {
                            
                            if (interactionAgent.getHas_Hability().contains(h)) {
                                habilities = new ArrayList<String>();
                                Hability hability = (Hability) h;
                                habilities.add(hability.getHabilityName());
                                logger.info("Hability super: " + hability.getName());
                            }
                        }
                        
                        this.serviceHabilities.put(service.getServiceName(),
                                habilities);
                    }
                }
                
            }
            
        } catch (Throwable e) {
            
        } finally {
            for (Object s : interactionAgent.getHas_Service()) {
                Service service = (Service) s;
                logger.info("Service:" + service.getName());
                
                if (service.getHas_Hability() != null) {
                    for (Object h : service.getHas_Hability()) {
                        
                        if (interactionAgent.getHas_Hability().contains(h)) {
                            habilities = new ArrayList<String>();
                            Hability hability = (Hability) h;
                            habilities.add(hability.getHabilityName());
                            
                            logger.info("Hability: " + hability.getName() );
                        }
                    }
                    this.serviceHabilities.put(service.getServiceName(), habilities);
                }
            }
        }
        getAgentServicesHabilities().put(this, serviceHabilities);
    }
    
    /**
     * Add the new behaviours.
     */
    public void registerServices() {
        
        PersistenceOWLLog persistenceLogAux = new PersistenceOWLLog();
        try {
            SharedMem mem = (SharedMem) Naming.lookup(RegionServer.HOST_URL);
            persistenceLogAux = (PersistenceOWLLog) mem.getRegion(Constants.OWLPersistence).retrieveIfExist(persistenceLogAux);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        br.ufal.ic.forbile.infra.interaction.ontology.Agent interactionAgent = (br.ufal.ic.forbile.infra.interaction.ontology.Agent)
        persistenceLogAux.getIndividualOWL();
        
        configureInteraction(interactionAgent);
        
        registerAgentServices(this);
    }
    
    /**
     * Gets the agent's habilities.
     *
     * @return The agent's habilities.
     */
    public Map<Agent, Map<String, List<String>>> getAgentServicesHabilities() {
        return agentServicesHabilities;
    }
    
    /**
     *  Search the specific agents according to the service and hability it.
     * @param service The agent's specific service.
     * @param hability The agent's specific hability.
     * @return All AID of the agents were found.
     */
    private AID[] searchAgent(final String service,
            final String hability) {
        
        ServiceDescription serviceDescription = new ServiceDescription();
        serviceDescription.setName( service );
        serviceDescription.setType( hability );
        
        DFAgentDescription agentDescription = new DFAgentDescription();
        agentDescription.addServices(serviceDescription);
        try {
            DFAgentDescription results[] = DFService.search(this,agentDescription);
            AID[] aid = new AID[results.length];
            for (int i = 0; i < results.length; i++) {
                aid[i] = results[i].getName();
            }
            return aid;
        } catch (FIPAException e){
            logger.info("Returns null");
            return null;
        }
    }
    
    // Z implementation ----------------------------------------------
    
    // setup overrides -----------------------------------------------
    
    protected void setup(){
        if (this instanceof PersistenceOWLAgentInteraction) {
            registerAgentServices(this);
        } else {
            requestPersistence();
        }
        sensor();      
        //addBehaviour(new GeneralActuator(getControllerAgent(), "", "START"));
    }
    
    // Package protected ---------------------------------------------
    
    // Protected -----------------------------------------------------
    /**
     * Observe the environment according to SENSOR_TIME.
     */
    protected abstract void sensor();
    
    /**
     * Set the characteristic of the interaction agent.
     */
    protected void requestPersistence() {
        AID[] aid = searchAgent("RETRIEVE", "INTERACTION");
        while ( aid == null ) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        logger.info("Interaction Agent: " + aid[0]);
        
        PersistenceOWLLog persistenceOWLLog = new PersistenceOWLLog();
        
        persistenceOWLLog.setClass_("Agent");
        persistenceOWLLog.setIndividual(this.getAgentLocalName());        
        persistenceOWLLog.setOwlFile("interaction");
        persistenceOWLLog.setRetrieveKey("Individual");
        try {
            SharedMem mem = (SharedMem) Naming.lookup(RegionServer.HOST_URL);
            mem.getRegion(Constants.OWLPersistence).write(persistenceOWLLog);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        addBehaviour(new RequestPersistence("RETRIEVE", aid[0]));
    };
    
    protected AID getMediatorAgent() {
        
        boolean hasServices = false;
        AID aid = null;
        
        while (!(hasServices)) {
            try {
                aid = searchAgent("MANAGE", null)[0];
                hasServices = true;
            } catch (Exception e) {                
                new ForBILEException("Waiting for MediatorAgent Services", e);
            }
            
        }
        
        return aid;
    }
    
    protected AID getControllerAgent(){
        
        boolean hasServices = false;
        AID aid = null;
        
        while (!(hasServices)) {
            try {
                aid = searchAgent("CONTROL", null)[0];
                hasServices = true;
            } catch (Exception e) {
                new ForBILEException("Waiting for ControllerAgent Services", e);
            }

        }
        return aid;
    }
    
    // Private -------------------------------------------------------
    
    /**
     * Gets the class name.
     *
     * @return The class.
     */
    private String getClassName(){
        String _class = this.getClass().getName();
        
        if (_class.contains(".")) {
            int lastIndex = _class.lastIndexOf(".");
            return _class.substring(lastIndex + 1);
        } else {
            return _class;
        }
    }
    
    // Inner classes -------------------------------------------------
    
}
